////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"

GraphicsClass::GraphicsClass() : m_D3D(0), m_Camera(0), m_Model(0), m_LightShader(0), m_Light(0)
{ }

GraphicsClass::GraphicsClass(const GraphicsClass& other)
{ }

GraphicsClass::~GraphicsClass()
{ }

bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
	bool result;
	D3DXMATRIX	baseViewMatrix;		// For text rendering.
	
	m_clearcolor[0] = 0.45f;		// Set initial clear colour.
	m_clearcolor[1] = 0.45f;
	m_clearcolor[2] = 0.45f;

	// Create and initialize the Direct3D object.
	m_D3D = new D3DClass;	
	if (!m_D3D)
		return false;

	result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, 
		SCREEN_DEPTH, SCREEN_NEAR);

	if (!Check_result(result, L"Could not initialize Direct3D")) return false;

	// Create the camera object and set its position.
	m_Camera = new CameraClass;
	if (!m_Camera)
		return false;

	m_Camera->SetPosition(0.0f, 0.0f, -20.0f);
	m_Camera->Render();
	m_Camera->GetViewMatrix(baseViewMatrix);
	m_Camera->GetViewMatrix(m_guiView);
	
	// Create and initialize the model object.
	m_Model = new ModelClass;
	if (!m_Model) 
		return false;

	result = m_Model->Initialize(m_D3D->GetDevice(), "bulat-research-project/graphics/models/cube.txt", 
		L"bulat-research-project/graphics/textures/checker.dds");
	if (!Check_result(result, L"Could not initialize the model object.")) 
		return false;

	// Create and initialize the light shader object.
	m_LightShader = new LightShaderClass;
	if (!m_LightShader)
		return false;

	result = m_LightShader->Initialize(m_D3D->GetDevice(), hwnd);
	if (!Check_result(result, L"Could not initialize the light shader object.")) 
		return false;

	// Create and initialize the light object.
	m_Light = new LightClass;
	if (!m_Light) 
		return false;
	
	m_Light->SetAmbientColor(0.15f, 0.15f, 0.15f, 1.0f);
	m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
	m_Light->SetDirection(0.0f, 0.0f, 1.0f);
	m_Light->SetSpecularColor(1.0f, 1.0f, 1.0f, 1.0f);
	m_Light->SetSpecularPower(32.0f);

	// Create and initialize the texture shader object.
	m_TextureShader = new TextureShaderClass;
	if (!m_TextureShader)
		return false;

	result = m_TextureShader->Initialize(m_D3D->GetDevice(), hwnd);
	if (!Check_result(result, L"Could not initialize texture shader object."))
		return false;

	// Create and initialize the bitmap object.
	m_Bitmap = new BitmapClass;
	if (!m_Bitmap)
		return false;

	result = m_Bitmap->Initialize(m_D3D->GetDevice(), screenWidth, screenHeight,
		L"bulat-research-project/graphics/textures/commandgui.dds", 1024, 32);
	if (!Check_result(result, L"Could not initialize bitmap object."))
		return false;

	// Create the text object.
	m_Text = new TextClass;
	if (!m_Text)
		return false;

	// Initialize the text object.
	result = m_Text->Initialize(m_D3D->GetDevice(), m_D3D->GetDeviceContext(), hwnd, screenWidth,
		screenHeight, baseViewMatrix);

	if (!result)
	{
		MessageBox(hwnd, L"Could not initialize the text object.", L"Error", MB_OK);
		return false;
	}

	return true;
}

void GraphicsClass::Shutdown()
{
	// Release the text object.
	if (m_Text)
	{
		m_Text->Shutdown();
		delete m_Text;
		m_Text = nullptr;
	}

	// Release the bitmap object.
	if (m_Bitmap)
	{
		m_Bitmap->Shutdown();
		delete m_Bitmap;
		m_Bitmap = nullptr;
	}

	// Release the texture shader object.
	if (m_TextureShader)
	{
		m_TextureShader->Shutdown();
		delete m_TextureShader;
		m_TextureShader = nullptr;
	}

	// Release the light object.
	if(m_Light)
	{
		delete m_Light;
		m_Light = nullptr;
	}

	// Release the light shader object.
	if (m_LightShader)
	{
		m_LightShader->Shutdown();
		delete m_LightShader;
		m_LightShader = nullptr;
	}

	// Release the model object.
	if (m_Model)
	{
		m_Model->Shutdown();
		delete m_Model;
		m_Model = nullptr;
	}

	// Release the camera object.
	if (m_Camera)
	{
		delete m_Camera;
		m_Camera = nullptr;
	}

	// Release the Direct3D object.
	if (m_D3D)
	{
		m_D3D->Shutdown();
		delete m_D3D;
		m_D3D = nullptr;
	}

	return;
}

bool GraphicsClass::Frame(int mouseX, int mouseY, vector<char>& inputb, string& command)
{
	bool result;

	// Set the location of the mouse.
	result = m_Text->SetMousePosition(mouseX, mouseY, m_D3D->GetDeviceContext());
	if (!result)
		return false;

	// Set the command line text.
	result = m_Text->SetCommandLine(inputb, m_D3D->GetDeviceContext());
	if (!result)
		return false;

	// Update bitmap buffer if necessary.
	result = m_Bitmap->UpdateBuffers(m_D3D->GetDeviceContext(), 0, 768-32);
	if (!result)
		return false;

	// Process command.
	ProcessCommand(command);

	return true;
}

bool GraphicsClass::Render(float rotation)
{
	D3DXMATRIX viewMatrix, projectionMatrix, worldMatrix;
	D3DXMATRIX identity, textWorld, orthoMatrix;
	bool result;

	// Clear the buffers to begin the scene.
	m_D3D->BeginScene(m_clearcolor[0], m_clearcolor[1], m_clearcolor[2], 1.0f);

	// Generate the view matrix based on the camera's position.
	m_Camera->Render();

	// Get the world, view, and projection matrices from the camera and d3d objects.
	m_Camera->GetViewMatrix(viewMatrix);
	m_D3D->GetWorldMatrix(worldMatrix);
	m_D3D->GetProjectionMatrix(projectionMatrix);
	m_D3D->GetOrthoMatrix(orthoMatrix);

	//float guiXPos = 1024.0f - (static_cast<float>(256 + 10));
	//D3DXMatrixTranslation(&guiWorld, guiXPos, -10.0f, 0.0f);	// World for GUI texture (pixels).
	D3DXMatrixTranslation(&textWorld, 72.0f, 0.0f, 0.0f);		// World for text rendering.

	D3DXMatrixIdentity(&identity);

	//
	//	3D Rendering
	//

	// Rotate the world matrix by the rotation value so that the triangle will spin.
	D3DXMatrixRotationY(&worldMatrix, rotation);

	// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
	m_Model->Render(m_D3D->GetDeviceContext());

	// Render the model using the light shader.
	result = m_LightShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(),
		worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(),
		m_Light->GetDirection(), m_Light->GetAmbientColor(), m_Light->GetDiffuseColor(),
		m_Camera->GetPosition(), m_Light->GetSpecularColor(), m_Light->GetSpecularPower());
	
	if (!result)
		return false;

	//
	//	2D Rendering
	//
	
	m_D3D->TurnZBufferOff();		// Turn off the Z buffer to begin all 2D rendering.
	
	m_D3D->TurnOnAlphaBlending();	// Turn on the alpha blending before rendering the text.

	result = m_Bitmap->Render(m_D3D->GetDeviceContext());	// Activate bitmap for rendering.
	
	if (!result)
		return false;
	
	result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Bitmap->GetIndexCount(),
		identity, m_guiView, orthoMatrix, m_Bitmap->GetTexture());
									
	if (!result)					// Render the bitmap with the texture shader.
		return false;
	
	result = m_Text->Render(m_D3D->GetDeviceContext(), orthoMatrix);
	if (!result)
		return false;				// Render the text strings.
	
	m_D3D->TurnOffAlphaBlending();	// Turn off alpha blending after rendering the text.
	
	m_D3D->TurnZBufferOn();			// Turn the Z Buffer back on now that all 2D rendering has completed.
	
	m_D3D->EndScene();				// Present the rendered scene to the screen.

	return true;
}

void GraphicsClass::ProcessCommand(const string& command)
{
	if (command == m_previousCommand)
		return;

	if (command == "black")
	{
		m_clearcolor[0] = 0.0f;
		m_clearcolor[1] = 0.0f;
		m_clearcolor[2] = 0.0f;
	}
	else if (command == "grey")
	{
		m_clearcolor[0] = 0.45f;
		m_clearcolor[1] = 0.45f;
		m_clearcolor[2] = 0.45f;
	}
	else if (command == "blue")
	{
		m_clearcolor[0] = 0.41f;
		m_clearcolor[1] = 0.41f;
		m_clearcolor[2] = 0.58f;
	}
	else
	{
		// Unrecognized command.
		// Process exit command in system class.
	}

	m_previousCommand = command;
}

void GraphicsClass::Write_GPUInfo()
{
	// Write graphics card name and memory to text file.
	char gpu_name[128];
	int  gpu_memory = 0;
	m_D3D->GetVideoCardInfo(gpu_name, gpu_memory);
	
	std::ofstream f;
	f.open(L"GPU_Info.txt");		// write to GPU_Info.txt
	f << "GPU Name: " << gpu_name << '\n' << "GPU Memory (MB): " << gpu_memory << '\n';	
	f.close();
}

bool GraphicsClass::Check_result(bool result, PWSTR message) const
{
	if (!result)
	{
		MessageBox(NULL, message, L"Error", MB_OK);
		
		return false;
	}

	return true;
}